﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Text;
using System.Data.SqlClient;
using System.Web.UI;
using NLog;
using System.Web.SessionState;

namespace TrackMoney
{
    public static class Login   
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();

        public static StringBuilder ConvertResultToJson(Result ret)
        {
            StringBuilder sb = new StringBuilder();
            //sb.Append("[");
            {
                sb.Append("{");
                sb.Append(" \"ret\":\"" + ret.ret.ToString() + "\"," + " \"msg\":\"" + ret.str +  "\"");
                sb.Append("}");
                
            }
            //sb.Append("]");

            return sb;
        }

        /// <summary>
        /// Get the password
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public static string FetPassword(string email)
        {
            string psd = "";
            string connStr = sql.GetSQL(sql.SQL.S_CONNECTION_STR);
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            string sqlStr = sql.GetSQL(sql.SQL.S_FETCH_PASSWROD);
            string sqlQuery = String.Format(sqlStr, email);

            SqlCommand cmd = new SqlCommand(sqlQuery, conn);
            cmd.CommandType = CommandType.Text;
            try
            {
                SqlDataReader reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    psd = reader.IsDBNull(0) ? "" : reader.GetString(0) ;
                }
                reader.Close();
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
            }
            catch (Exception ex)
            {
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                logger.Error(ex.Message);
            }
            return psd;
        }
        /// <summary>
        /// Fetch user id 
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public static int FetchId(string email)
        {
            int id = 0;
            string connStr = sql.GetSQL(sql.SQL.S_CONNECTION_STR);
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            string sqlStr = sql.GetSQL(sql.SQL.S_FETCH_ID);
            string sqlQuery = String.Format(sqlStr, email);

            SqlCommand cmd = new SqlCommand(sqlQuery, conn);
            cmd.CommandType = CommandType.Text;
            try
            {
                SqlDataReader reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    id = reader.IsDBNull(0) ? 0 : reader.GetInt32(0);
                }
                reader.Close();
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
            }
            catch (Exception ex)
            {
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                logger.Error(ex.Message);
            }
            return id;
        }
        /// <summary>
        /// update the password
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static bool UpdatePassword(string id, string p)
        {
            bool result = false;
            string connStr = sql.GetSQL(sql.SQL.S_CONNECTION_STR);
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            string sqlStr = sql.GetSQL(sql.SQL.S_UPDATE_PASSWORD);
            //acName, acEmail, acPassword, acAge, acGender
            string sqlQuery = String.Format(sqlStr, id, p);
            SqlCommand cmd = new SqlCommand(sqlQuery, conn);
            cmd.CommandType = CommandType.Text;
            try
            {
                cmd.ExecuteNonQuery();
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                result = true;
            }
            catch (Exception ex)
            {
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                logger.Error(ex.Message);
                result = false;
            }

            return result;
        }
        /// <summary>
        /// Save the user info. to db
        /// </summary>
        /// <param name="ac"></param>
        /// <returns></returns>
        public static bool SaveSignUp(UserAccount ac)
        {
            bool result = false;
            string connStr = sql.GetSQL(sql.SQL.S_CONNECTION_STR);
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            string sqlStr = sql.GetSQL(sql.SQL.S_SAVE_SIGN_UP);
            //acName, acEmail, acPassword, acAge, acGender
            string sqlQuery = String.Format(sqlStr, ac.name, ac.email, ac.password, ac.age, ac.gender);
            SqlCommand cmd = new SqlCommand(sqlQuery, conn);
            cmd.CommandType = CommandType.Text;
            try
            {
                cmd.ExecuteNonQuery();
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                result = true;
            }
            catch (Exception ex)
            {
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                logger.Error(ex.Message);
                result = false;
            }

            return result;
        }

    }
    /// <summary>
    /// General class services all others
    /// </summary>
    public static class G
    {
        public enum eCategory
        {
            eCaIncome = 1,
            eCaExpense,
            eCaCategory
        }
        /// <summary>
        /// get the start and end period
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="iPeriod"></param>
        public static void GetPeriod(ref DateTime start, ref DateTime end, int iPeriod)
        {
            end = start = DateTime.Now;
            switch (iPeriod)
            {
                //get all records
                case -1:
                    start = Convert.ToDateTime(DateTime.Now.ToString("1900-1-1 00:00:00"));
                    end = Convert.ToDateTime(DateTime.Now.ToString("9999-1-1 23:59:59"));
                    break;
                //today
                case 0:
                    start = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd 00:00:00"));
                    end = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd 23:59:59"));
                    break;
                //yesterday
                case 1:
                    start = Convert.ToDateTime(DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd 00:00:00"));
                    end = Convert.ToDateTime(DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd 23:59:59"));
                    break;
                //this week
                case 2:
                    DateTime dTime = DateTime.Now;
                    int index = (int)dTime.DayOfWeek;
                    index = (index == 0 ? 7 : index);
                    start = dTime.AddDays(-(index - 1));
                    end = dTime.AddDays(7 - index);
                    //start = Convert.ToDateTime(DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd 00:00:00"));
                    //end = Convert.ToDateTime(DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd 23:59:59"));
                    break;
                //last week
                case 3:
                    dTime = DateTime.Now;
                    index = (int)dTime.DayOfWeek;
                    index = (index == 0 ? 7 : index);
                    start = dTime.AddDays(-(index - 1)).AddDays(-7);
                    end = dTime.AddDays(7 - index).AddDays(-7);
                    //start = Convert.ToDateTime(DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd 00:00:00"));
                    //end = Convert.ToDateTime(DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd 23:59:59"));
                    break;
                //this month
                case 4:
                    start = Convert.ToDateTime(DateTime.Parse(DateTime.Now.ToString("yyyy-MM-01 00:00:00")).ToShortDateString());
                    end = Convert.ToDateTime(DateTime.Parse(DateTime.Now.ToString("yyyy-MM-01 23:59:59")).AddMonths(1).AddDays(-1).ToString("yyyy-MM-dd 23:59:59"));
                    break;
                //last month
                case 5:
                    start = Convert.ToDateTime(DateTime.Parse(DateTime.Now.ToString("yyyy-MM-01 00:00:00")).AddMonths(-1).ToShortDateString());
                    end = Convert.ToDateTime(DateTime.Parse(DateTime.Now.ToString("yyyy-MM-01 23:59:59")).AddDays(-1).ToString("yyyy-MM-dd 23:59:59"));
                    break;
                //this year
                case 6:
                    start = Convert.ToDateTime(DateTime.Parse(DateTime.Now.ToString("yyyy-01-01 00:00:00")).ToShortDateString());
                    end = Convert.ToDateTime(DateTime.Parse(DateTime.Now.ToString("yyyy-01-01 23:59:59")).AddYears(1).AddDays(-1).ToString("yyyy-MM-dd 23:59:59"));
                    break;
                //last year
                case 7:
                    start = Convert.ToDateTime(DateTime.Parse(DateTime.Now.ToString("yyyy-01-01 00:00:00")).AddYears(-1).ToShortDateString());
                    end = Convert.ToDateTime(DateTime.Parse(DateTime.Now.ToString("yyyy-01-01 23:59:59")).AddDays(-1).ToString("yyyy-MM-dd 23:59:59"));
                    break;
                case 8:
                    start = Convert.ToDateTime(("1800-01-01 00:00:00"));
                    end = Convert.ToDateTime(("9999-01-01 00:00:00"));
                    break;
            }
        }
        /// <summary>
        /// Get prefix of a specified table
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public static string getTablePrefix(eCategory e)
        {
            string sPrefix = "";
            switch (e)
            {
                case eCategory.eCaCategory:
                    sPrefix = "ca";
                    break;
                case eCategory.eCaExpense:
                    sPrefix = "ep";
                    break;
                case eCategory.eCaIncome:
                    sPrefix = "in";
                    break;
            }

            return sPrefix;
        }
        /// <summary>
        /// get sql state
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public static sql.SQL GetSQLByCategory(eCategory e)
        {
            sql.SQL sqlstate = sql.SQL.S_GET_INCOME;
            switch (e)
            {
                case eCategory.eCaExpense:
                    sqlstate = sql.SQL.S_GET_EXPENSE;
                    break;
                case eCategory.eCaIncome:
                    sqlstate = sql.SQL.S_GET_INCOME;
                    break;
            }
            return sqlstate;
        }
        /// <summary>
        /// Get name of db by category
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public static string getDBByCategory(eCategory e)
        {
            string sPrefix = "";
            switch (e)
            {
                case eCategory.eCaCategory:
                    sPrefix = "fzCategories";
                    break;
                case eCategory.eCaExpense:
                    sPrefix = "fzExpenses";
                    break;
                case eCategory.eCaIncome:
                    sPrefix = "fzIncomes";
                    break;
            }

            return sPrefix;
        }
        /// <summary>
        /// Get the category by current step
        /// </summary>
        /// <param name="step"></param>
        /// <returns></returns>
        public static eCategory getCategoryByStep(string step)
        {
            eCategory e = eCategory.eCaCategory;
            switch (step)
            {
                case "AddExpense":
                    e = eCategory.eCaExpense;
                    break;
                case "AddIncome":
                    e = eCategory.eCaIncome;
                    break;
                case "AddCategory":
                    e = eCategory.eCaCategory;
                    break;
            }
            return e;
        }
    }
    /// <summary>
    /// Class income
    /// </summary>
    public static class OpIncome
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();
        
        private static List<Income> incomes = new List<Income>();


        public static StringBuilder ConvertIncomeToJson(int start, int end, string step, int iPeriod, int sid)
        {
            List<Income> ins = GetIncomes(start, end, step, iPeriod, sid);
            int total = GetIncomeTotal(step, iPeriod, sid);
            StringBuilder sb = new StringBuilder();
            sb.Append("{\"total\":\""+total.ToString()+"\",data:");
            sb.Append("[");
            for (int i = 0; i < ins.Count; i++)
            {
                sb.Append("{");

                sb.Append(" \"key\":\"" + ins[i].key.ToString() + "\"," + " \"amount\":\"" + ins[i].amount.ToString() + "\"," + "\"category\":\"" + ins[i].sCategory + "\"," + " \"Date\":\"" + ins[i].date.ToString("dd/MM/yyyy") + "\"," + " \"Notes\":\"" + ins[i].notes + "\"");
                sb.Append("}");
                if (i != ins.Count - 1)
                {
                    sb.Append(",");
                }
            }
            sb.Append("]");
            sb.Append("}");

            return sb;
        }
        /// <summary>
        /// get the total of income records
        /// </summary>
        /// <returns></returns>
        private static int GetIncomeTotal(string step, int iPeriod, int id)
        {
            int total = 0;
            DateTime start = DateTime.Now;
            DateTime end = start;
            G.GetPeriod(ref start, ref end, iPeriod);
            G.eCategory e = G.getCategoryByStep(step);
            string sPrefix = G.getTablePrefix(e);

            string dbName = G.getDBByCategory(G.getCategoryByStep(step));
            string connStr = sql.GetSQL(sql.SQL.S_CONNECTION_STR);
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            string sqlStr = sql.GetSQL(sql.SQL.S_GET_INCOME_TOTAL);

            string sqlQuery = String.Format(sqlStr, dbName, start.ToString("yyyy-MM-dd 00:00:00"), end.ToString("yyyy-MM-dd 23:59:59"), sPrefix, id);

            SqlCommand cmd = new SqlCommand(sqlQuery, conn);
            cmd.CommandType = CommandType.Text;
            try
            {
                SqlDataReader reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    total = reader.IsDBNull(0) ? 0 : reader.GetInt32(0);
                }
                reader.Close();
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
            }
            catch (Exception ex)
            {
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                logger.Error(ex.Message);
            }
            return total;
        }
        

        /// <summary>
        /// get all incomes
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        private static List<Income> GetIncomes(int start, int end, string step, int iPeriod, int sid)
        {
            DateTime startPeriod = DateTime.Now;
            DateTime endPeriod = DateTime.Now;
            G.eCategory e = G.getCategoryByStep(step);
            string dbName = G.getDBByCategory(e);
            string sPrefix = G.getTablePrefix(e);
            string where = "";
            if (iPeriod != -1)
            {
                G.GetPeriod(ref startPeriod, ref endPeriod, iPeriod);
                where = " and " + sPrefix + "Date >='" + startPeriod.ToString("yyyy-MM-dd 00:00:00") + "' and " + sPrefix + "Date <='" + endPeriod.ToString("yyyy-MM-dd 23:59:59")+"'";
            }
            string connStr = sql.GetSQL(sql.SQL.S_CONNECTION_STR);
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            string sqlStr = sql.GetSQL(G.GetSQLByCategory(e));
            string userFilter = sPrefix + "Owner = " + sid;
            string sqlQuery = String.Format(sqlStr, end-start, start, where, userFilter);

            SqlCommand cmd = new SqlCommand(sqlQuery, conn);
            cmd.CommandType = CommandType.Text;
            try
            {
                SqlDataReader reader = cmd.ExecuteReader();
                incomes.Clear();
                while (reader.Read())
                {
                    Income income = new Income();
                    income.key = reader.IsDBNull(0) ? 0 : reader.GetInt32(0);
                    income.category = reader.IsDBNull(1) ? 0 : reader.GetInt32(1);
                    income.amount = reader.IsDBNull(2) ? (decimal)0.0 : decimal.Parse(reader.GetDecimal(2).ToString("F2"));
                    income.date = reader.IsDBNull(3) ? DateTime.Now : reader.GetDateTime(3);
                    income.notes = reader.IsDBNull(4) ? "" : reader.GetString(4);
                    if (income.notes.Length > App.appData.NotesMaxLen)
                    {
                        income.notes = income.notes.Substring(0, App.appData.NotesMaxLen);
                    }
                    income.group = reader.IsDBNull(5) ? 0 : reader.GetInt32(5);
                    income.owner = reader.IsDBNull(6) ? 0 : reader.GetInt32(6);
                    income.sCategory = reader.IsDBNull(7) ? "Unknown" : reader.GetString(7);
                    incomes.Add(income);
                }
                reader.Close();
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
            }
            catch (Exception ex)
            {
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                logger.Error(ex.Message);
            }

            return incomes;
        }

        /// <summary>
        /// Add a new income
        /// </summary>
        /// <param name="record"></param>
        /// <returns></returns>
        public static bool AddIncome(string record, string step, int sid)
        {
            bool result = false;
            string[] rd = record.Split('|');
            G.eCategory e = G.getCategoryByStep(step);
            
            string connStr = sql.GetSQL(sql.SQL.S_CONNECTION_STR);
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            string sqlStr = "";
            if (e == G.eCategory.eCaIncome) { sqlStr = sql.GetSQL(sql.SQL.S_ISSUE_INCOME);}
            if (e == G.eCategory.eCaExpense) { sqlStr = sql.GetSQL(sql.SQL.S_ISSUE_EXPENSE); }
             
            //inCategory, inAmount, inDate, inNotes
            string sqlQuery = String.Format(sqlStr, rd[0], rd[1], DateTime.Parse(rd[2]).ToString("yyyy-MM-dd HH:mm:ss"), rd[3], rd[4], sid);

            SqlCommand cmd = new SqlCommand(sqlQuery, conn);
            cmd.CommandType = CommandType.Text;
            try
            {
                cmd.ExecuteNonQuery();
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                result = true;
            }
            catch (Exception ex)
            {
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                logger.Error(ex.Message);
                result = false;
            }

            return result;
        }
    }
    /// <summary>
    /// Class category
    /// </summary>
    public static class OpCategory
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();
        
        private static List<category> ca = new List<category>();

        
        /// <summary>
        /// convert the category list to json
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static StringBuilder ConvertCategoryToJson(int start, int end, int type, int sid)
        {
            List<category> categories = GetCategories(start, end, type, sid);
            StringBuilder sb = new StringBuilder();
            sb.Append("[");
            for (int i = 0; i < categories.Count; i++)
            {
                sb.Append("{");

                sb.Append(" \"key\":\"" + categories[i].key.ToString() + "\"," + " \"name\":\"" + categories[i].name + "\"," + "\"type\":\""+categories[i].type.ToString() + "\"");
                sb.Append("}");
                if (i != categories.Count - 1)
                {
                    sb.Append(",");
                }
            }
            sb.Append("]");

            return sb;
        }

        /// <summary>
        /// get all categories
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        private static List<category> GetCategories(int start, int end, int type, int sid)
        {
            string connStr = sql.GetSQL(sql.SQL.S_CONNECTION_STR);
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            string sqlStr = sql.GetSQL(sql.SQL.S_GET_CATEGORY);
            string query = " 1=1  and caOwner=" + sid;
            if (type != 0)
            {
                query = " caType = " + type.ToString() ;
            }
            string sqlQuery = String.Format(sqlStr, end, query);

            SqlCommand cmd = new SqlCommand(sqlQuery, conn);
            cmd.CommandType = CommandType.Text;
            try
            {
                SqlDataReader reader = cmd.ExecuteReader();
                ca.Clear();
                while (reader.Read())
                {
                    category c = new category();
                    c.key = reader.IsDBNull(0) ? 0 : reader.GetInt32(0);
                    c.name = reader.IsDBNull(1) ? "Unknown" : reader.GetString(1);
                    c.type = reader.IsDBNull(2) ? 0 : reader.GetInt32(2);
                    ca.Add(c);
                }
                reader.Close();
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
            }
            catch (Exception ex)
            {
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                logger.Error(ex.Message);
            }

            return ca;
        }
        /// <summary>
        /// Get the summary according to a certain period
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        public static string GetSummary(DateTime start, DateTime end, G.eCategory e, int sid)
        {
            decimal data = 0;
            
            string connStr = sql.GetSQL(sql.SQL.S_CONNECTION_STR);
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            sql.SQL sqlState = sql.SQL.S_GET_SUMMARY_INCOME;
            if (e == G.eCategory.eCaExpense) sqlState = sql.SQL.S_GET_SUMMARY_EXPENSE;
            if (e == G.eCategory.eCaIncome) sqlState = sql.SQL.S_GET_SUMMARY_INCOME;
            string sqlStr = sql.GetSQL(sqlState);
            string sqlQuery = String.Format(sqlStr, start.ToString("yyyy-MM-dd 00:00:00"), end.ToString("yyyy-MM-dd 23:59:59"), sid);

            SqlCommand cmd = new SqlCommand(sqlQuery, conn);
            cmd.CommandType = CommandType.Text;
            try
            {
                SqlDataReader reader = cmd.ExecuteReader();
                if (reader.Read())
                {
                    data = reader.IsDBNull(0) ? 0 : reader.GetDecimal(0);
                }
               
                reader.Close();
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
            }
            catch (Exception ex)
            {
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                //debug
                data = 99;
                logger.Error(ex.Message);
            }

            return data.ToString();
        }
        /// <summary>
        /// delete a category or many
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public static bool DeleteRecord(string[] ids, string step)
        {
            bool result = false;
            string where = "";
            G.eCategory e = G.getCategoryByStep(step);
            string sPrefix = G.getTablePrefix(e);
            for(int i=0; i<ids.Length; i++)
            {
                where += sPrefix + "Key=" + ids[i];
                if(i != ids.Length-1)
                {
                    where += " or ";
                }
            }
            string dbName = G.getDBByCategory(e);
            string connStr = sql.GetSQL(sql.SQL.S_CONNECTION_STR);
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            string sqlStr = sql.GetSQL(sql.SQL.S_DEL_CATEGORY);
            
            string sqlQuery = String.Format(sqlStr, dbName, where);

            SqlCommand cmd = new SqlCommand(sqlQuery, conn);
            cmd.CommandType = CommandType.Text;
            try
            {
                cmd.ExecuteNonQuery();
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                result = true;
            }
            catch (Exception ex)
            {
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                logger.Error(ex.Message);
                result = false;
            }

            return result;
        }
        /// <summary>
        /// add a new category
        /// </summary>
        /// <param name="record"></param>
        public static bool AddCategory(string record, int sid)
        {
            bool result = false;
            string[] rd = record.Split('|');
            
            string connStr = sql.GetSQL(sql.SQL.S_CONNECTION_STR);
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            string sqlStr = sql.GetSQL(sql.SQL.S_ISSUE_CATEGORY);

            string sqlQuery = String.Format(sqlStr, rd[0], rd[1], sid);

            SqlCommand cmd = new SqlCommand(sqlQuery, conn);
            cmd.CommandType = CommandType.Text;
            try
            {
                cmd.ExecuteNonQuery();
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                result = true;
            }
            catch (Exception ex)
            {
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
                logger.Error(ex.Message);
                result = false;
            }

            return result;
        }
    }
}